/*-------------------------------------------------------------------------
 *
 * parse_param.c
 *	  handle parameters in parser
 *
 * 该代码涵盖了在核心后端中使用的两种情况：
 *		* 一组具有已知类型的固定参数
 *		* 一组可扩展参数，这些参数的类型可以从上下文中确定
 * 在这两种情况下，仅支持显式的 $n 引用（ParamRef 节点）。
 *
 * 注意，使用在 ParseState 中定义的解析器钩子可以实现参数的其他方法。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_param.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <limits.h>

#include "catalog/pg_type.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_param.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"


typedef struct FixedParamState
{
	const Oid  *paramTypes;		/* 参数类型OID数组 */
	int			numParams;		/* 数组条目的数量 */
} FixedParamState;

/*
 * 在varparams情况下，调用方提供的OID数组（如果有）可以在需要时重新分配更大的空间。零数组条目表示该参数编号尚未被看到，而UNKNOWNOID表示该参数已被使用但其类型尚未知。
 */
typedef struct VarParamState
{
	Oid		  **paramTypes;		/* 参数类型OID数组 */
	int		   *numParams;		/* 数组条目的数量 */
} VarParamState;

static Node *fc_fixed_paramref_hook(ParseState *fc_pstate, ParamRef *fc_pref);
static Node *fc_variable_paramref_hook(ParseState *fc_pstate, ParamRef *fc_pref);
static Node *fc_variable_coerce_param_hook(ParseState *fc_pstate, Param *fc_param,
										Oid fc_targetTypeId, int32 fc_targetTypeMod,
										int fc_location);
static bool fc_check_parameter_resolution_walker(Node *fc_node, ParseState *fc_pstate);
static bool fc_query_contains_extern_params_walker(Node *fc_node, void *fc_context);


/*
 * 设置以处理包含固定参数引用的查询。
 */
void setup_parse_fixed_parameters(ParseState *fc_pstate,
							 const Oid *fc_paramTypes, int fc_numParams)
{
	FixedParamState *fc_parstate = palloc(sizeof(FixedParamState));

	fc_parstate->paramTypes = fc_paramTypes;
	fc_parstate->numParams = fc_numParams;
	fc_pstate->p_ref_hook_state = (void *) fc_parstate;
	fc_pstate->p_paramref_hook = fc_fixed_paramref_hook;
	/* 不需要使用 p_coerce_param_hook */
}

/*
 * 设置以处理包含可变参数引用的查询。
 */
void setup_parse_variable_parameters(ParseState *fc_pstate,
								Oid **fc_paramTypes, int *fc_numParams)
{
	VarParamState *fc_parstate = palloc(sizeof(VarParamState));

	fc_parstate->paramTypes = fc_paramTypes;
	fc_parstate->numParams = fc_numParams;
	fc_pstate->p_ref_hook_state = (void *) fc_parstate;
	fc_pstate->p_paramref_hook = fc_variable_paramref_hook;
	fc_pstate->p_coerce_param_hook = fc_variable_coerce_param_hook;
}

/*
 * 使用固定参数类型转换ParamRef。
 */
static Node * fc_fixed_paramref_hook(ParseState *fc_pstate, ParamRef *fc_pref)
{
	FixedParamState *fc_parstate = (FixedParamState *) fc_pstate->p_ref_hook_state;
	int			fc_paramno = fc_pref->number;
	Param	   *fc_param;

	/* 检查参数编号是否有效 */
	if (fc_paramno <= 0 || fc_paramno > fc_parstate->numParams ||
		!OidIsValid(fc_parstate->paramTypes[fc_paramno - 1]))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_PARAMETER),
				 errmsg("there is no parameter $%d", fc_paramno),
				 parser_errposition(fc_pstate, fc_pref->location)));

	fc_param = makeNode(Param);
	fc_param->paramkind = PARAM_EXTERN;
	fc_param->paramid = fc_paramno;
	fc_param->paramtype = fc_parstate->paramTypes[fc_paramno - 1];
	fc_param->paramtypmod = -1;
	fc_param->paramcollid = get_typcollation(fc_param->paramtype);
	fc_param->location = fc_pref->location;

	return (Node *) fc_param;
}

/*
 * 使用可变参数类型转换ParamRef。
 *
 * 唯一的区别是我们必须根据需要扩大参数类型数组。
 */
static Node * fc_variable_paramref_hook(ParseState *fc_pstate, ParamRef *fc_pref)
{
	VarParamState *fc_parstate = (VarParamState *) fc_pstate->p_ref_hook_state;
	int			fc_paramno = fc_pref->number;
	Oid		   *fc_pptype;
	Param	   *fc_param;

	/* 检查参数编号是否在范围内 */
	if (fc_paramno <= 0 || fc_paramno > INT_MAX / sizeof(Oid))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_PARAMETER),
				 errmsg("there is no parameter $%d", fc_paramno),
				 parser_errposition(fc_pstate, fc_pref->location)));
	if (fc_paramno > *fc_parstate->numParams)
	{
		/* 需要扩大参数数组 */
		if (*fc_parstate->paramTypes)
			*fc_parstate->paramTypes = (Oid *) repalloc(*fc_parstate->paramTypes,
													 fc_paramno * sizeof(Oid));
		else
			*fc_parstate->paramTypes = (Oid *) palloc(fc_paramno * sizeof(Oid));
		/* 将以前未引用的插槽置零 */
		MemSet(*fc_parstate->paramTypes + *fc_parstate->numParams,
			   0,
			   (fc_paramno - *fc_parstate->numParams) * sizeof(Oid));
		*fc_parstate->numParams = fc_paramno;
	}

	/* 在数组中定位参数的插槽 */
	fc_pptype = &(*fc_parstate->paramTypes)[fc_paramno - 1];

	/* 如果之前未见，初始化为UNKNOWN类型 */
	if (*fc_pptype == InvalidOid)
		*fc_pptype = UNKNOWNOID;

	/*
	 * 如果参数为void类型且为过程调用，将其解释为未知类型。这允许JDBC驱动程序不必区分函数和过程调用。另请参阅ParseFuncOrColumn()中的此hack的其他部分。
	 */
	if (*fc_pptype == VOIDOID && fc_pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT)
		*fc_pptype = UNKNOWNOID;

	fc_param = makeNode(Param);
	fc_param->paramkind = PARAM_EXTERN;
	fc_param->paramid = fc_paramno;
	fc_param->paramtype = *fc_pptype;
	fc_param->paramtypmod = -1;
	fc_param->paramcollid = get_typcollation(fc_param->paramtype);
	fc_param->location = fc_pref->location;

	return (Node *) fc_param;
}

/*
 * 在varparams情况下，将Param强制转换为查询请求的数据类型。
 */
static Node * fc_variable_coerce_param_hook(ParseState *fc_pstate, Param *fc_param,
						   Oid fc_targetTypeId, int32 fc_targetTypeMod,
						   int fc_location)
{
	if (fc_param->paramkind == PARAM_EXTERN && fc_param->paramtype == UNKNOWNOID)
	{
		/*
		 * 输入是类型先前未确定的Param，我们想更新对Param类型的了解。
		 */
		VarParamState *fc_parstate = (VarParamState *) fc_pstate->p_ref_hook_state;
		Oid		   *fc_paramTypes = *fc_parstate->paramTypes;
		int			fc_paramno = fc_param->paramid;

		if (fc_paramno <= 0 ||		/* 不应该发生，但... */
			fc_paramno > *fc_parstate->numParams)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_PARAMETER),
					 errmsg("there is no parameter $%d", fc_paramno),
					 parser_errposition(fc_pstate, fc_param->location)));

		if (fc_paramTypes[fc_paramno - 1] == UNKNOWNOID)
		{
			/* 我们已成功解析了类型 */
			fc_paramTypes[fc_paramno - 1] = fc_targetTypeId;
		}
		else if (fc_paramTypes[fc_paramno - 1] == fc_targetTypeId)
		{
			/* 我们之前解析了类型，并且它匹配 */
		}
		else
		{
			/* Oops */
			ereport(ERROR,
					(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
					 errmsg("inconsistent types deduced for parameter $%d",
							fc_paramno),
					 errdetail("%s versus %s",
							   format_type_be(fc_paramTypes[fc_paramno - 1]),
							   format_type_be(fc_targetTypeId)),
					 parser_errposition(fc_pstate, fc_param->location)));
		}

		fc_param->paramtype = fc_targetTypeId;

		/*
		 * 注意：这里诱人将Param的paramtypmod设置为targetTypeMod，但这可能是不明智的，因为我们没有任何基础设施来强制Param提供的值将匹配任何特定的typmod。保持为-1确保在需要时会发生运行时长度检查/强制转换。
		 */
		fc_param->paramtypmod = -1;

		/*
		 * 本模块始终将Param的排序规则设置为其数据类型的默认值。如果这不是您想要的，您应该使用更通用的解析器替换钩子。
		 */
		fc_param->paramcollid = get_typcollation(fc_param->paramtype);

		/* 使用参数和强制转换的最左位置 */
		if (fc_location >= 0 &&
			(fc_param->location < 0 || fc_location < fc_param->location))
			fc_param->location = fc_location;

		return (Node *) fc_param;
	}

	/* 否则发出信号以正常强制转换 */
	return NULL;
}

/*
 * 检查在完成解析 parse_variable_parameters 后变量参数的一致赋值。
 *
 * 注意：此代码故意不检查所有参数位置是否都被使用，或者所有参数是否都被赋予了非UNKNOWN类型。调用解析器的代码
 * 应在该情况重要时强制执行。
 */
void check_variable_parameters(ParseState *fc_pstate, Query *fc_query)
{
	VarParamState *fc_parstate = (VarParamState *) fc_pstate->p_ref_hook_state;

	/* 如果 numParams 为零，则没有生成 Params，因此没有工作 */
	if (*fc_parstate->numParams > 0)
		(void) query_tree_walker(fc_query,
								 fc_check_parameter_resolution_walker,
								 (void *) fc_pstate, 0);
}

/*
 * 遍历一个完全分析的树，以验证参数符号是否与其类型匹配。我们需要这样做，因为某些 Params 可能仍然是
 * UNKNOWN，如果没有任何东西强制它们的强制转换，而其他在稍后看到的实例可能已经被强制转换。
 */
static bool fc_check_parameter_resolution_walker(Node *fc_node, ParseState *fc_pstate)
{
	if (fc_node == NULL)
		return false;
	if (IsA(fc_node, Param))
	{
		Param	   *fc_param = (Param *) fc_node;

		if (fc_param->paramkind == PARAM_EXTERN)
		{
			VarParamState *fc_parstate = (VarParamState *) fc_pstate->p_ref_hook_state;
			int			fc_paramno = fc_param->paramid;

			if (fc_paramno <= 0 || /* 不应该发生，但... */
				fc_paramno > *fc_parstate->numParams)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_PARAMETER),
						 errmsg("there is no parameter $%d", fc_paramno),
						 parser_errposition(fc_pstate, fc_param->location)));

			if (fc_param->paramtype != (*fc_parstate->paramTypes)[fc_paramno - 1])
				ereport(ERROR,
						(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
						 errmsg("could not determine data type of parameter $%d",
								fc_paramno),
						 parser_errposition(fc_pstate, fc_param->location)));
		}
		return false;
	}
	if (IsA(fc_node, Query))
	{
		/* 递归进入RTE子查询或尚未规划的子链接子查询 */
		return query_tree_walker((Query *) fc_node,
								 fc_check_parameter_resolution_walker,
								 (void *) fc_pstate, 0);
	}
	return expression_tree_walker(fc_node, fc_check_parameter_resolution_walker,
								  (void *) fc_pstate);
}

/*
 * 检查完全解析的查询树是否包含任何 PARAM_EXTERN Params。
 */
bool query_contains_extern_params(Query *fc_query)
{
	return query_tree_walker(fc_query,
							 fc_query_contains_extern_params_walker,
							 NULL, 0);
}

static bool fc_query_contains_extern_params_walker(Node *fc_node, void *fc_context)
{
	if (fc_node == NULL)
		return false;
	if (IsA(fc_node, Param))
	{
		Param	   *fc_param = (Param *) fc_node;

		if (fc_param->paramkind == PARAM_EXTERN)
			return true;
		return false;
	}
	if (IsA(fc_node, Query))
	{
		/* 递归进入RTE子查询或尚未规划的子链接子查询 */
		return query_tree_walker((Query *) fc_node,
								 fc_query_contains_extern_params_walker,
								 fc_context, 0);
	}
	return expression_tree_walker(fc_node, fc_query_contains_extern_params_walker,
								  fc_context);
}
